﻿#region License
/*

File Upload HTTP module for ASP.Net (v 2.0)
Copyright (C) 2007-2008 Darren Johnstone (http://darrenjohnstone.net)

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/
#endregion

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

namespace CoyoEden.Uploader
{
	/// <summary>
	/// Multiple file upload control with progress bar.
	/// </summary>
	public class DJFileUpload : WebControl, INamingContainer
	{
		#region Member variables

		DJUploadController _controller;
		int DEFAULT_INITIAL = 1;
		int DEFAULT_MAXIMUM = 5;

		int _initialFileUploads;
		int _maxFileUploads;
		bool _showUploadButton;
		bool _showAddButton;
		bool _requiredField;
		string _requiredMessage = "请至少选择一个待上传的文件";
		IFileProcessor _processor;

		internal static string UPLOAD_PARAMETER_TAG = "::DJ_UPLOAD_PARAMETER::";
		internal static string UPLOAD_END_TAG = "::DJ_UPLOAD_END::";

		List<System.Web.UI.WebControls.FileUpload> _uploadControls;
		HiddenField _parameters;
		HiddenField _endMarker;

		#endregion

		#region Properties
		/// <summary>
		/// Gets/sets a custom file processor which will override the global settings
		/// in UploadManager.
		/// </summary>
		public IFileProcessor FileProcessor {
			get { return _processor; }
			set {
				_processor = value as IFileProcessor;

				if (_processor == null) {
					throw new ArgumentException("File processor must implement IFileProcessor");
				}
			}
		}

		/// <summary>
		/// Gets/sets a boolean value indicating if at least one upload file is required for this control.
		/// </summary>
		public bool RequiredField {
			get { return _requiredField; }
			set { _requiredField = value; }
		}

		/// <summary>
		/// Gets/sets the required field validation message.
		/// </summary>
		public string RequiredMesage {
			get { return _requiredMessage; }
			set { _requiredMessage = value; }
		}

		/// <summary>
		/// Gets or sets the initial number of file uploads.
		/// </summary>
		/// <value>Gets or sets the initial number of file uploads.</value>
		public int InitialFileUploads {
			get { return _initialFileUploads; }
			set { _initialFileUploads = value; }
		}

		/// <summary>
		/// Gets or sets the maximum number of file uploads.
		/// </summary>
		/// <value>Gets or sets the maximum number of file uploads.</value>
		public int MaxFileUploads {
			get { return _maxFileUploads; }
			set { _maxFileUploads = value; }
		}

		/// <summary>
		/// Gets or sets a value indicating whether the upload button should be shown.
		/// </summary>
		/// <value><c>true</c> if the upload button should be shown; otherwise, <c>false</c>.</value>
		public bool ShowUploadButton {
			get { return _showUploadButton; }
			set { _showUploadButton = value; }
		}

		/// <summary>
		/// Gets or sets a value indicating whether the add button should be shown.
		/// </summary>
		/// <value><c>true</c> if the add button should be shown; otherwise, <c>false</c>.</value>
		public bool ShowAddButton {
			get { return _showAddButton; }
			set { _showAddButton = value; }
		}
		#endregion

		#region .ctor
		/// <summary>
		/// Initializes a new instance of the <see cref="DJFileUpload"/> class.
		/// </summary>
		public DJFileUpload() {
		}
		#endregion

		#region base override
		/// <summary>
		/// Raises the <see cref="E:System.Web.UI.Control.Init"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
		protected override void OnInit(EventArgs e) {
			base.OnInit(e);

			if (InitialFileUploads <= 0) {
				InitialFileUploads = DEFAULT_INITIAL;
			}

			if (MaxFileUploads <= 0) {
				MaxFileUploads = DEFAULT_MAXIMUM;
			}
		}

		/// <summary>
		/// Called by the ASP.NET page framework to notify server controls that use 
		/// composition-based implementation to create any child controls they contain 
		/// in preparation for posting back or rendering.
		/// </summary>
		protected override void CreateChildControls() {
			base.CreateChildControls();

			_controller = GetController();
			_uploadControls = new List<System.Web.UI.WebControls.FileUpload>();

			// Create the parameter field
			_parameters = new HiddenField();
			Controls.Add(_parameters);

			// Create the container
			Panel outerContainer = new Panel();
			outerContainer.CssClass = "upUploadBox";
			Controls.Add(outerContainer);

			// Create the file uploads
			for (int i = 0; i < MaxFileUploads; i++) {
				Panel fuContainer = new Panel();
				fuContainer.CssClass = "upFileInputs";
				outerContainer.Controls.Add(fuContainer);

				System.Web.UI.WebControls.FileUpload fu = new System.Web.UI.WebControls.FileUpload();
				_uploadControls.Add(fu);
				fu.CssClass = "upFile";
				fuContainer.Controls.Add(fu);

				ImageButton btnRemove = new ImageButton();
				fuContainer.Controls.Add(btnRemove);
				btnRemove.AlternateText = "Remove upload";
				btnRemove.CssClass = "btnremove";
				btnRemove.ImageUrl = _controller.ImagePath + "removebutton.gif";
				btnRemove.OnClientClick = "up_RemoveUpload(this); return false;";

				if (i >= InitialFileUploads) {
					fuContainer.Style.Add("display", "none");
				}
			}

			// Create the buttons
			ImageButton btnGo = new ImageButton();
			outerContainer.Controls.Add(btnGo);
			btnGo.AlternateText = "Upload now";
			btnGo.ImageUrl = _controller.ImagePath + "uploadbutton.gif";
			btnGo.Visible = ShowUploadButton;
			btnGo.CausesValidation = true;

			ImageButton btnAdd = new ImageButton();
			outerContainer.Controls.Add(btnAdd);
			btnAdd.AlternateText = "Add a new upload";
			btnAdd.ImageUrl = _controller.ImagePath + "addbutton.gif";
			btnAdd.OnClientClick = "up_AddUpload('" + ClientID + "'); return false;";
			btnAdd.Visible = ShowAddButton;

			CustomValidator val = new CustomValidator();
			val.ServerValidate += new ServerValidateEventHandler(val_ServerValidate);
			val.ClientValidationFunction = "up_ValidateUpload";
			val.ErrorMessage = _requiredMessage;
			val.Enabled = _requiredField;
			val.EnableClientScript = _requiredField;
			Controls.Add(val);

			// Create the end marker
			_endMarker = new HiddenField();
			Controls.Add(_endMarker);
		}
		/// <summary>
		/// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
		protected override void OnPreRender(EventArgs e) {
			base.OnPreRender(e);

			if (_processor != null && UploadManager.Instance.ModuleInstalled) {
				_parameters.Value = UPLOAD_PARAMETER_TAG + UploadManager.Instance.SerializeProcessor(_processor);
				_endMarker.Value = UPLOAD_END_TAG;
			}
		}
		#endregion

		#region helper methods
		/// <summary>
		/// Handles the ServerValidate event of the val control.
		/// </summary>
		/// <param name="source">The source of the event.</param>
		/// <param name="args">The <see cref="System.Web.UI.WebControls.ServerValidateEventArgs"/> instance containing the event data.</param>
		void val_ServerValidate(object source, ServerValidateEventArgs args) {
			bool ok = false;

			EnsureChildControls();
			foreach (System.Web.UI.WebControls.FileUpload fu in _uploadControls) {
				if (fu.FileName != String.Empty) {
					ok = true;
					break;
				}
			}

			args.IsValid = ok;
		}
		/// <summary>
		/// Gets the upload controller.
		/// </summary>
		/// <returns>The upload controller.</returns>
		DJUploadController GetController() {
			DJUploadController res = null;

			foreach (object o in Page.Form.Controls) {
				res = o as DJUploadController;

				if (res != null)
					break;
			}

			if (res == null) {
				throw new Exception("An instance of the DJUploadController control must be placed at the beginning of the page before other controls.");
			}

			return res;
		}
		#endregion
	}
}
